﻿using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;

namespace Red.Scripts.Project.Generator.Project
{
    public class Reader
    {
        private XDocument _contents;

        private Dictionary<string, IProperty> _properties = new Dictionary<string, IProperty>();

        #region Input

        public string SourceFile { get; set; } = @"Red.Scripts.redproj";

        #endregion Input

        #region Output
        public Dictionary<string, IProperty> Properties => _properties;

        #endregion Output

        public bool Load()
        {
            try
            {
                _contents = XDocument.Load( SourceFile );
            }
            catch ( FileNotFoundException )
            {
                Log.WriteLine( "No project file found" );
                return false;
            }

            if ( !AddValue< Guid >( "ProjectGuid" ) )
                return false;

            // General
            AddValue<string>( "ScriptsRoot" );
            AddValue<string>( "ScriptsBinary" );
            AddValue<bool>( "DisableAsserts" );
            AddValue<int>( "SccThreads" );
            AddValue<string>( "SccProfilingMode" );

            // Deploy
            AddValue<bool>( "SkipDeploy" );
            AddValue<string>( "RedScriptsPathToCmdlet" );
            AddValue<string>( "RedScriptsCmdletCWD" );
            AddValue<string>( "GameDef" );
            AddValue<string>( "CameraLocation" );
            AddValue<string>( "CameraRotation" );

            // Debugging
            AddValue<string>( "RedScriptsPathToLauncher" );
            AddValue<string>( "RedScriptsCommandLineArguments" );
            AddValue<string>( "RedScriptsCWD" );
            AddValue<string>( "RedEngineType" );
            AddValue<string>( "SaveFile" );

            return true;
        }

        private bool AddValue< T >( string name )
        {
            Property<T> property;

            if( GetValue( name, out property ) )
            {
                _properties[ name ] = property;

                return true;
            }

            return false;
        }

        public bool GetValue( string name, out string value )
        {
            try
            {
                XElement element = _contents.DescendantsNS( name ).First();
                value = element.Value;
                return true;
            }
            catch ( InvalidOperationException )
            {
                Log.WriteErrorLine( $"Property '{name}' missing from project file" );
            }

            value = null;
            return false;
        }

        public bool GetValue<T>( string name, out Property<T> value )
        {
            value = new Property<T>();

            string raw;
            string label;
            try
            {
                XElement element = _contents.DescendantsNS( name ).First();
                raw = element.Value;

                label = element.Parent.Attribute( "Label" )?.Value;
            }
            catch ( InvalidOperationException )
            {
                Log.WriteErrorLine( $"Property '{name}' missing from project file" );
                return false;
            }


            if ( GetValue( name, out raw ) )
            {
                Type type = typeof( T );
                TypeConverter converter = TypeDescriptor.GetConverter( type );

                if ( converter.IsValid( raw ) )
                {
                    object converted = converter.ConvertFromString( raw );

                    value.IsSet = true;
                    value.Name = name;
                    value.Label = label;
                    value.Value = (T)converted;

                    return true;
                }
            }

            return false;
        }
    }
}
